/* Console example — various system commands

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "esp_log.h"
#include "esp_console.h"
#include "esp_system.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "driver/uart.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/rtc_cntl_reg.h"
#include "rom/uart.h"
#include "sdkconfig.h"
#include "driver/ledc.h"
#include "driver/uart.h"

#define ECHO_TEST_TXD  (GPIO_NUM_17)
#define ECHO_TEST_RXD  (GPIO_NUM_16)
#define ECHO_TEST_RTS  (UART_PIN_NO_CHANGE)
#define ECHO_TEST_CTS  (UART_PIN_NO_CHANGE)

#define BUF_SIZE (80)

#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
#define WITH_TASKS_INFO 1
#endif
#define LEDC_HS_MODE           LEDC_HIGH_SPEED_MODE         //高速组
#define LEDC_HS_TIMER          LEDC_TIMER_0                 //LEDC计时器0
#define LEDC_HS_CH0_CHANNEL    LEDC_CHANNEL_0               //通道0


static void register_free();
static void register_restart();
static void register_deep_sleep();
static void register_light_sleep();
static void register_pwm_();
static void register_led_();
static void register_uart();
//#if WITH_TASKS_INFO
static void register_tasks();

//#endif

void register_system()
{
    register_free();            //查看可用内存
    register_restart();         //重启
    register_deep_sleep();      //休眠
    register_light_sleep();     //
    register_pwm_();            //pwm 控制
    register_led_();            //LEDC控制
    register_uart();
    //register_pwm_2();
//#if WITH_TASKS_INFO
    register_tasks();           //查看任务列表
//#endif
}

/** 'restart' command restarts the program */

static int restart(int argc, char** argv)
{
    ESP_LOGI(__func__, "Restarting");
    esp_restart();
}

static void register_restart()
{
    const esp_console_cmd_t cmd = {
        .command = "restart",
        .help = "重启esp32",
        .hint = NULL,
        .func = &restart,
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static struct {
    struct arg_int *freq_hz;
    struct arg_int *duty ;
    struct arg_int *time;
    struct arg_end *end;
} pwm_args;

void pwm_init(uint32_t hz,uint16_t duty,uint8_t gpio){
    ledc_timer_config_t ledc_timer = {
        .duty_resolution = LEDC_TIMER_10_BIT, // PWM信号的占空比
        .freq_hz = hz,                      // PWM信号的频率
        .speed_mode = LEDC_HS_MODE,           // 计时器模式
        .timer_num = LEDC_HS_TIMER            // 计时器索引
    };
    ledc_timer_config(&ledc_timer);

    ledc_channel_config_t ledc_channel={
            .channel    = LEDC_HS_CH0_CHANNEL,      //通道数
            .duty       = duty,                        //占空比，初始化10
            .gpio_num   = gpio,                        //gpio号
            .speed_mode = LEDC_HS_MODE,             //速度模式，高速
            .timer_sel  = LEDC_HS_TIMER             //选用高速计时器
        };
    
    ledc_channel_config(&ledc_channel);
}

/*LEDC ‘pwm’ 命令*/
static int pwm_(int argc, char** argv)
{
    uint32_t hz_=0;
    uint16_t duty_=0;
    double du;
    int nerrors = arg_parse(argc, argv, (void**) &pwm_args);
    if (nerrors != 0) {
        arg_print_errors(stderr, pwm_args.end, argv[0]);
        return 1;
    }
    if (pwm_args.freq_hz->count) {
        hz_ =  pwm_args.freq_hz->ival[0];
        //ESP_LOGI(__func__, "Enabling freq, %d",ledc_timer.freq );
    }

   if (pwm_args.duty->count) {
        duty_=  pwm_args.duty->ival[0];
        //ESP_LOGI(__func__, "Enabling duty, %d",ledc_channel.duty );
    }
    pwm_init(hz_,duty_,4);
    du=duty_/10.24;
    ESP_LOGI(__func__, "PWM test!freq:%dHz ,duty: %f",hz_,du);
    //ledc_channel_config(&ledc_channel);
    //ledc_update_duty(LEDC_HS_MODE, LEDC_HS_CH0_CHANNEL);
    return 0;
}

static void register_pwm_()
{
    pwm_args.freq_hz =
            arg_int0("f", "freq", "<n>", "pwm freq Hz.");
    pwm_args.duty =
            arg_int0("d", "duty", "<n>", "pwm duty.");
    pwm_args.time =
            arg_int0("t", "time", "<t>", "pwm time.");
    pwm_args.end = arg_end(3);

    const esp_console_cmd_t cmd = {
        .command = "pwm",
        .help = "pwm out freq duty time ",
        .hint = "freq duty time",
        .func = &pwm_,
        .argtable = &pwm_args
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

static struct {
    struct arg_int *freq_hz;
    struct arg_int *duty ;
    struct arg_end *end;
} led_args;

static int led(int argc, char** argv)
{
    uint32_t hz_=0;
    uint16_t duty_=0;
    double du=0.0;

    int nerrors = arg_parse(argc, argv, (void**) &led_args);
    if (nerrors != 0) {
        arg_print_errors(stderr, led_args.end, argv[0]);
        return 1;
    }
    if (led_args.freq_hz->count) {
        hz_ =  led_args.freq_hz->ival[0];
        //ESP_LOGI(__func__, "Enabling freq, %d",ledc_timer.freq );
    }

   if (led_args.duty->count) {
        duty_=  led_args.duty->ival[0];
        //ESP_LOGI(__func__, "Enabling duty, %d",ledc_channel.duty );
    }
    pwm_init(hz_,duty_,2);
    du=duty_/10.24;
    ESP_LOGI(__func__, "LED test!freq:%dHz ,duty: %2.2f",hz_,du);
    //ledc_channel_config(&ledc_channel);
    //ledc_update_duty(LEDC_HS_MODE, LEDC_HS_CH0_CHANNEL);
    return 0;

}

static void register_led_()
{
    led_args.freq_hz =
            arg_int0("f", "freq", "<n>", "led freq Hz.");
    led_args.duty =
            arg_int0("d", "duty", "<n>", "led duty.");
    led_args.end = arg_end(2);

    const esp_console_cmd_t cmd = {
        .command = "led",
        .help = "LEDC out freq duty ",
        .hint = "freq duty",
        .func = &led,
        .argtable = &led_args
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );

}
static bool uart_init = 0;
static struct {
    struct arg_lit *read;
    struct arg_str *write ;
    struct arg_end *end;
} uart_args;
void init_uart(int baud_rate){
    if(uart_init == 0){
        uart_config_t uart_config = {
        .baud_rate = baud_rate,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
        };
        uart_param_config(UART_NUM_1, &uart_config);
        uart_set_pin(UART_NUM_1, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS);
        uart_driver_install(UART_NUM_1, BUF_SIZE * 2, 0, 0, NULL, 0);
        uart_init=1;
    }

}

static int uart(int argc, char** argv){
    char * fxx = NULL;
    int len=0;
    int nerrors = arg_parse(argc, argv, (void**) &uart_args);
    if (nerrors != 0) {
        arg_print_errors(stderr, uart_args.end, argv[0]);
        return 1;
    }
    init_uart(9600);
    if(uart_init ==1){
            if(uart_args.write->count){
                
                //len=sscanf( write_data,"%s%c%c%c",uart_args.write->sval[0],&fxx,&fxx,&fxx);
                //strcat(uart_args.write->sval[0],fxx);
                len = sprintf(fxx,"%s \xff\xff\xff",uart_args.write->sval[0]);
                uart_write_bytes(UART_NUM_1, (const char *) fxx, len);
                //uart_write_bytes(UART_NUM_1, (const char *) fxx, 3);
                //uart_write_bytes(UART_NUM_1, (const char *) xff, strlen(xff));
                usleep(100000);
                uint8_t * data = (uint8_t *) malloc(BUF_SIZE);
                memset(data,0,BUF_SIZE);
                len=uart_read_bytes(UART_NUM_1, data, BUF_SIZE, 20 / portTICK_RATE_MS);
                if(len==0) printf("no data!\n");
                else{
                    printf("uart read %d bite: %s\n",len,data);
                        for(uint8_t i=0;i<len;i++) 
                            printf("%02x:%c ",*(data+i),*(data+i));
                    printf("\n");
                }
                free(data);
            }
            if(uart_args.read->count){
                uint8_t * data = (uint8_t *) malloc(BUF_SIZE);
                memset(data,0,BUF_SIZE);
                len=uart_read_bytes(UART_NUM_1, data, BUF_SIZE, 20 / portTICK_RATE_MS);
                if(len==0) printf("no data!\n");
                else{
                    printf("uart read %d bite: %s\n",len,data);
                        for(uint8_t i=0;i<len;i++) 
                            printf("%02x:%c ",*(data+i),*(data+i));
                    printf("\n");
                }
                free(data);
               
            }
    }
    return 0;
}

static void register_uart(){
    uart_args.read =arg_lit0("r", "read","read uart.");
    uart_args.write=arg_str0("w","write","<str>","write data to uart");
    uart_args.end=arg_end(2);

    const esp_console_cmd_t cmd = {
        .command = "uart",
        .help = "uart read//write data test. ",
        .hint = "read | write",
        .func = &uart,
        .argtable = &uart_args
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );

}

/** 'free' command prints available heap memory */

static int free_mem(int argc, char** argv)
{
    printf("当前空闲堆内存:%db,可用的最小空闲堆:%db\n", esp_get_free_heap_size(),esp_get_minimum_free_heap_size());
    return 0;
}

static void register_free()
{
    const esp_console_cmd_t cmd = {
        .command = "free",
        .help = "获取可用堆内存的总大小.",
        .hint = NULL,
        .func = &free_mem,
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}



/** 'tasks' command prints the list of tasks and related information */
//#if WITH_TASKS_INFO

static int tasks_info(int argc, char** argv)
{
    const size_t bytes_per_task = 40; /* see vTaskList description */
    char* task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task);
    if (task_list_buffer == NULL) {
        ESP_LOGE(__func__, "failed to allocate buffer for vTaskList output");
        return 1;
    }
    fputs("Task名称\t运行状态优先级\t剩余栈\t任务序号#", stdout);
/* #ifdef CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
    fputs("\tAffinity", stdout);
#endif */
    fputs("\n", stdout);
    vTaskList(task_list_buffer);
    fputs(task_list_buffer, stdout);
/*     fputs("\r\nTask名称\t运行计数\t使用率\r\n", stdout);
    vTaskGetRunTimeStats(task_list_buffer);
    fputs(task_list_buffer, stdout); */
    free(task_list_buffer);
    return 0;
}

static void register_tasks()
{
    const esp_console_cmd_t cmd = {
        .command = "tasks",
        .help = "获取关于运行任务的信息",
        .hint = NULL,
        .func = &tasks_info,
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

//#endif // WITH_TASKS_INFO

/** 'deep_sleep' command puts the chip into deep sleep mode */

static struct {
    struct arg_int *wakeup_time;
    struct arg_int *wakeup_gpio_num;
    struct arg_int *wakeup_gpio_level;
    struct arg_end *end;
} deep_sleep_args;


static int deep_sleep(int argc, char** argv)
{
    int nerrors = arg_parse(argc, argv, (void**) &deep_sleep_args);
    if (nerrors != 0) {
        arg_print_errors(stderr, deep_sleep_args.end, argv[0]);
        return 1;
    }
    if (deep_sleep_args.wakeup_time->count) {
        uint64_t timeout = 1000ULL * deep_sleep_args.wakeup_time->ival[0];
        ESP_LOGI(__func__, "Enabling timer wakeup, timeout=%lluus", timeout);
        ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
    }
    if (deep_sleep_args.wakeup_gpio_num->count) {
        int io_num = deep_sleep_args.wakeup_gpio_num->ival[0];
        if (!rtc_gpio_is_valid_gpio(io_num)) {
            ESP_LOGE(__func__, "GPIO %d is not an RTC IO", io_num);
            return 1;
        }
        int level = 0;
        if (deep_sleep_args.wakeup_gpio_level->count) {
            level = deep_sleep_args.wakeup_gpio_level->ival[0];
            if (level != 0 && level != 1) {
                ESP_LOGE(__func__, "Invalid wakeup level: %d", level);
                return 1;
            }
        }
        ESP_LOGI(__func__, "Enabling wakeup on GPIO%d, wakeup on %s level",
                io_num, level ? "HIGH" : "LOW");

        ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup(1ULL << io_num, level) );
    }
    rtc_gpio_isolate(GPIO_NUM_12);
    esp_deep_sleep_start();
}

static void register_deep_sleep()
{
    deep_sleep_args.wakeup_time =
            arg_int0("t", "time", "<t>", "Wake up time, ms");
    deep_sleep_args.wakeup_gpio_num =
            arg_int0(NULL, "io", "<n>","If specified, wakeup using GPIO with given number");
    deep_sleep_args.wakeup_gpio_level =
            arg_int0(NULL, "io_level", "<0|1>", "GPIO level to trigger wakeup");
    deep_sleep_args.end = arg_end(3);

    const esp_console_cmd_t cmd = {
        .command = "deep_sleep",
        .help = "进入深度睡眠模式。 "
                "支持两种唤醒模式：定时器和GPIO。 "
                "如果未指定唤醒选项，则将无限期地休眠。",
        .hint = NULL,
        .func = &deep_sleep,
        .argtable = &deep_sleep_args
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

/** 'light_sleep' command puts the chip into light sleep mode */

static struct {
    struct arg_int *wakeup_time;
    struct arg_int *wakeup_gpio_num;
    struct arg_int *wakeup_gpio_level;
    struct arg_end *end;
} light_sleep_args;

static int light_sleep(int argc, char** argv)
{
    int nerrors = arg_parse(argc, argv, (void**) &light_sleep_args);
    if (nerrors != 0) {
        arg_print_errors(stderr, light_sleep_args.end, argv[0]);
        return 1;
    }
    esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
    if (light_sleep_args.wakeup_time->count) {
        uint64_t timeout = 1000ULL * light_sleep_args.wakeup_time->ival[0];
        ESP_LOGI(__func__, "Enabling timer wakeup, timeout=%lluus", timeout);
        ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
    }
    int io_count = light_sleep_args.wakeup_gpio_num->count;
    if (io_count != light_sleep_args.wakeup_gpio_level->count) {
        ESP_LOGE(__func__, "Should have same number of 'io' and 'io_level' arguments");
        return 1;
    }
    for (int i = 0; i < io_count; ++i) {
        int io_num = light_sleep_args.wakeup_gpio_num->ival[i];
        int level = light_sleep_args.wakeup_gpio_level->ival[i];
        if (level != 0 && level != 1) {
            ESP_LOGE(__func__, "Invalid wakeup level: %d", level);
            return 1;
        }
        ESP_LOGI(__func__, "Enabling wakeup on GPIO%d, wakeup on %s level",
                io_num, level ? "HIGH" : "LOW");

        ESP_ERROR_CHECK( gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL) );
    }
    if (io_count > 0) {
        ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() );
    }
    if (CONFIG_CONSOLE_UART_NUM <= UART_NUM_1) {
        ESP_LOGI(__func__, "Enabling UART wakeup (press ENTER to exit light sleep)");
        ESP_ERROR_CHECK( uart_set_wakeup_threshold(CONFIG_CONSOLE_UART_NUM, 3) );
        ESP_ERROR_CHECK( esp_sleep_enable_uart_wakeup(CONFIG_CONSOLE_UART_NUM) );
    }
    fflush(stdout);
    uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
    esp_light_sleep_start();
    esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
    const char* cause_str;
    switch (cause) {
        case ESP_SLEEP_WAKEUP_GPIO:
            cause_str = "GPIO";
            break;
        case ESP_SLEEP_WAKEUP_UART:
            cause_str = "UART";
            break;
        case ESP_SLEEP_WAKEUP_TIMER:
            cause_str = "timer";
            break;
        default:
            cause_str = "unknown";
            printf("%d\n", cause);
    }
    ESP_LOGI(__func__, "Woke up from: %s", cause_str);
    return 0;
}

static void register_light_sleep()
{
    light_sleep_args.wakeup_time =
            arg_int0("t", "time", "<t>", "唤醒时间，ms");
    light_sleep_args.wakeup_gpio_num =
            arg_intn(NULL, "io", "<n>", 0, 8,
                     "如果指定，则使用给定数字的GPIO唤醒");
    light_sleep_args.wakeup_gpio_level =
            arg_intn(NULL, "io_level", "<0|1>", 0, 8, "GPIO级别触发唤醒");
    light_sleep_args.end = arg_end(3);

    const esp_console_cmd_t cmd = {
        .command = "light_sleep",
        .help = "进入轻度睡眠模式。 "
                "支持两种唤醒模式：定时器和GPIO。 "
                "可以使用成对的多个GPIO引脚来指定 "
                "'io' and 'io_level' 参数. "
                "也会唤醒UART输入。",
        .hint = NULL,
        .func = &light_sleep,
        .argtable = &light_sleep_args
    };
    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}

